package com.bjy.qa.service.dashboard.impl;

import com.bjy.qa.dao.dashboard.TestCaseCountDao;
import com.bjy.qa.dao.functionaltest.ApiDao;
import com.bjy.qa.dao.functionaltest.TestCaseDao;
import com.bjy.qa.dao.functionaltest.TestCaseRemarkDao;
import com.bjy.qa.dao.functionaltest.TestSuiteDao;
import com.bjy.qa.dao.user.UserCompanyProjectDao;
import com.bjy.qa.entity.dashboard.TestCaseCount;
import com.bjy.qa.enumtype.CatalogType;
import com.bjy.qa.service.dashboard.ITestCaseCountService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoUnit;
import java.util.*;

@Service
public class TestCaseCountService implements ITestCaseCountService {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Resource
    TestCaseCountDao testCaseCountDao;

    @Resource
    TestCaseDao testCaseDao;

    @Resource
    TestSuiteDao testSuiteDao;

    @Resource
    TestCaseRemarkDao testCaseRemarkDao;

    @Resource
    UserCompanyProjectDao userCompanyProjectDao;

    @Resource
    ApiDao apiDao;

    /**
     * 获取指定用户的新增修改，执行的用例数据
     * @param designer 操作者
     * @param projectId 项目ID
     */
    private void getTestCaseCount(String designer, Long projectId, String date) {
        TestCaseCount testCaseCount = new TestCaseCount(); // 构造一个 userTestCaseCount 对象，用来存放用户数据统计信息

        int addApiCases = testCaseDao.getAddCount(designer, projectId, CatalogType.INTERFACE_TEST_CASE, date); // 获取增加的接口测试用例数
        int updateApiCases = testCaseDao.getUpdateCount(designer, projectId, CatalogType.INTERFACE_TEST_CASE, date); // 获取更新的接口测试用例数
        int addFunCases = testCaseDao.getAddCount(designer, projectId, CatalogType.TEST_CASE, date); // 获取增加的功能测试用例数
        int updateFunCases = testCaseDao.getUpdateCount(designer, projectId, CatalogType.TEST_CASE, date); // 获取更新的功能测试用例数
        int addApiCount = apiDao.getAddApiCount(designer, projectId, date); // 获取新增的接口数
        int updateApiCount = apiDao.getUpdateApiCount(designer, projectId, date); // 获取修改的接口数

        List<Date> dateList = testCaseRemarkDao.selectDate(designer, projectId, date); // 查询指定设计师和项目的测试用例日期列表

        calculateTimeVariance(dateList, testCaseCount); // 获取总时间，执行用例平均耗时，标准差

        // 将统计数据设置到 userTestCaseCount 对象中
        testCaseCount.setDesigner(designer);
        testCaseCount.setProjectId(projectId);
        testCaseCount.setUpdateFunCases(updateFunCases);
        testCaseCount.setAddFunCases(addFunCases);
        testCaseCount.setAddApiCases(addApiCases);
        testCaseCount.setUpdateApiCases(updateApiCases);
        testCaseCount.setAddApiCount(addApiCount);
        testCaseCount.setUpdateApiCount(updateApiCount);

        testCaseCountDao.insert(testCaseCount); // 将查到的数据都存到数据库中
        logger.info("用户数据存库成功");
    }

    /**
     * 以用户的维度将用户在不同项目中用例的数据信息统计完成并存到库中
     * @return
     */
    public Object testCaseCount() {
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd"); // 创建一个日期格式化器
        String currentDate = LocalDate.now().format(dtf); // 获取当前日期，并使用日期格式化器将其格式化为字符串

        testCaseCountDao.deleteByDate(currentDate); // 调用该方法时会删除当天已经存在的数据

        List<Map<String, Object>> resultList = userCompanyProjectDao.getUserCompanyProjects();// 从 dashboardDao 获取用户和项目 id 的列表

        for (Map<String, Object> result : resultList) { // 从结果中获取用户姓名和项目 ID
            String designer = (String) result.get("name");
            Long projectId = (Long) result.get("project_id");

            getTestCaseCount(designer, projectId, currentDate); // getUserDataCounts() 方法，传递 name 和 projectId 来将项目中所有用户数据存库
        }
        return true;
    }

    /**
     * 计算执行用例的总耗时，标准差和平均耗时
     * @param dateList 日期列表
     * @param testCaseCount testCaseCount对象
     */
    private void calculateTimeVariance(List<Date> dateList, TestCaseCount testCaseCount) {
        long totalExecutionTime = 0; // 初始化执行用例的总耗时
        double execTimeStdDev = 0; // 用于存储执行用例的标准差
        double avgExecutionTime = 0; // 执行用例的平均耗时
        double sumDiffsSquare = 0; // // 用于存储时间差的平方和
        int count = 0; // 用于记录忽略的时间差数量
        int validDateCount = dateList.size() - 1; // 有效日期数量为总日期数量减 1
        int excFunCases = dateList.size(); // 获取执行的功能测试用例数

        for (int i = 0; i < dateList.size() - 1; i++) {
            Date date1 = dateList.get(i);
            Date date2 = dateList.get(i + 1);
            long diffInSeconds = Math.abs(date2.getTime() - date1.getTime()) / 1000;

            if (diffInSeconds >= 15 * 60) { // 如果时间大于15分钟，就舍弃该段时间
                count++;
            } else {
                totalExecutionTime += diffInSeconds;
                sumDiffsSquare += Math.pow(diffInSeconds, 2);
            }
        }

        validDateCount -= count; // 减去忽略的时间差数量，得到有效日期数量

        if (validDateCount != 0) { // 判断 validDateCount 为0的情况
            avgExecutionTime = (double) totalExecutionTime / validDateCount; // 计算平均耗时
            execTimeStdDev = Math.sqrt(sumDiffsSquare / validDateCount - Math.pow(avgExecutionTime, 2)); // 根据离散度公式求出标准差
        }
        testCaseCount.setFunTotalTime(totalExecutionTime);
        testCaseCount.setFunAvgTime(avgExecutionTime);
        testCaseCount.setFunVar(execTimeStdDev);
        testCaseCount.setExcFunCases(excFunCases);
    }

    @Override
    public Map<String, Object> getTestCaseCount(Long projectId, String startDate, String endDate) throws ParseException {
        LocalDate start;
        try {
            start = LocalDate.parse(startDate);
        } catch (DateTimeParseException e) {
            throw new RuntimeException("开始时间格式错误，应为：yyyy-MM-dd");
        }

        LocalDate end;
        try {
            end = LocalDate.parse(endDate);
        } catch (DateTimeParseException e) {
            throw new RuntimeException("结束时间格式错误，应为：yyyy-MM-dd");
        }

        if (start.isAfter(end)) {
            throw new RuntimeException("开始时间 必须小于或等于 结束时间。");
        }

        // 设置 x轴坐标（日期）
        List<String> dates = new ArrayList<>();
        for (LocalDate date = start; !date.isAfter(end); date = date.plusDays(1)) {
            dates.add(date.toString());
        }

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        sdf.setTimeZone(TimeZone.getTimeZone("GMT+8"));
        List<Map<String, Object>> testCaseCountData = testCaseCountDao.selectTestCaseCount(projectId, sdf.parse(start + " 00:00:00"), sdf.parse(end + " 23:59:59")); // 执行数据库查询，并处理结果集

        int totalDay = dates.size(); // 开始到结束之间的总天数用作数组的长度
        Map<String, Map<String, Object>> funSeries = new HashMap<>(); // 功能测试用例数据
        Map<String, Map<String, Object>> apiSeries = new HashMap<>(); // 接口测试用例数据
        Map<String, Map<String, Object>> totalTimeSeries = new HashMap<>(); // 执行用例总时间
        Map<String, Map<String, Object>> avgTimeSeries = new HashMap<>(); // 执行用例平均时间
        Map<String, Map<String, Object>> varSeries = new HashMap<>(); // 执行用例标准差
        Map<String, Map<String, Object>> apiCountSeries = new HashMap<>(); // 接口数据

        for (Map<String, Object> testCaseCount : testCaseCountData) { // 遍历 testCaseCountData 对象，拿到每一行 designer，created_at和对应的测试用例数据
            String designer = (String) testCaseCount.get("designer");
            String createdAt = testCaseCount.get("created_at").toString().substring(0, 10); // 获取到 created_at 然后将其转化成 "yyyy-mm-dd” 格式的日期字符串
            LocalDate date = LocalDate.parse(createdAt);

            int dateIndex = (int) ChronoUnit.DAYS.between(start, date); // 计算日期的索引，用来将数据插入数组中对应时间的位置
            long addApiCases = Long.valueOf(testCaseCount.get("add_api_cases").toString()).longValue();
            long updateApiCases = Long.valueOf(testCaseCount.get("update_api_cases").toString()).longValue();
            long addFunCases = Long.valueOf(testCaseCount.get("add_fun_cases").toString()).longValue();
            long updateFunCases = Long.valueOf(testCaseCount.get("update_fun_cases").toString()).longValue();
            long excFunCases = Long.valueOf(testCaseCount.get("exc_fun_cases").toString()).longValue();
            long funTotalTime = Long.valueOf(testCaseCount.get("fun_total_time").toString()).longValue();
            long addApiCount = Long.valueOf(testCaseCount.get("add_api_count").toString()).longValue();
            long updateApiCount = Long.valueOf(testCaseCount.get("update_api_count").toString()).longValue();
            double funAvgTime = Double.valueOf(testCaseCount.get("fun_avg_time").toString()).doubleValue();
            double funVar = Double.valueOf(testCaseCount.get("fun_var").toString()).doubleValue();

            longCount("_新增接口用例", designer, apiSeries, totalDay, dateIndex, addApiCases);
            longCount("_修改接口用例", designer, apiSeries, totalDay, dateIndex, updateApiCases);
            longCount("_新增功能用例", designer, funSeries, totalDay, dateIndex, addFunCases);
            longCount("_修改功能用例", designer, funSeries, totalDay, dateIndex, updateFunCases);
            longCount("_执行功能用例", designer, funSeries, totalDay, dateIndex, excFunCases);
            longCount("_执行功能用例总时间", designer, totalTimeSeries, totalDay, dateIndex, funTotalTime);
            longCount("_新增接口数", designer, apiCountSeries, totalDay, dateIndex, addApiCount);
            longCount("_修改接口数", designer, apiCountSeries, totalDay, dateIndex, updateApiCount);
            doubleData("_执行功能用例平均时间", designer, avgTimeSeries, totalDay, dateIndex, funAvgTime);
            doubleData("_执行功能用例标准差", designer, varSeries, totalDay, dateIndex, funVar);
        }

        Map<String, Object> caseCountMap = new HashMap<>(); // 创建一个新的 caseCountMap，该 caseCountMap 用于存储和返回要处理的数据结果
        caseCountMap.put("dateList", dates);
        caseCountMap.put("apiSeries", apiSeries.values().toArray());
        caseCountMap.put("funSeries", funSeries.values().toArray());
        caseCountMap.put("totalTimeSeries", totalTimeSeries.values().toArray());
        caseCountMap.put("avgTimeSeries", avgTimeSeries.values().toArray());
        caseCountMap.put("varSeries", varSeries.values().toArray());
        caseCountMap.put("apiCountSeries", apiCountSeries.values().toArray());
        return caseCountMap;
    }

    /**
     * 通过 type 和 designer 将统计到的数据放入 series 中（整数）
     * @param type 类型
     * @param designer 用户名
     * @param series 存储用户和用例类型的 map，其中包含用例数据的整型数组
     * @param totalDay 总天数，用来作为存储用例数的数组长度
     * @param dateIndex 指定日期的索引，将用例数存到指定的日期
     * @param count 用例数量
     */
    private void longCount(String type, String designer, Map<String, Map<String, Object>> series, int totalDay, int dateIndex, long count) {
        Map<String, Object> map = series.get(designer + type); // 从 series 获取数据（例如 key=张三_新增接口用例）
        if (map == null) { // 若果为空，创建一个新对象放入 map
            map = new HashMap<>();
            map.put("name", designer + type);
            map.put("stack", designer);
            long[] data = new long[totalDay]; // 用于存储对应日期中的数值
            data[dateIndex] = count; // 按照索引值将 data 插入数组
            map.put("data", data);
            series.put(designer + type, map);
        } else { // 不为空，插入对应日期的数值
            long[] data = (long[]) map.get("data");
            data[dateIndex] = count;
        }
    }

    /**
     * 通过 type 和 designer 将统计到的数据放入 series 中（小数）
     * @param type 类型
     * @param designer 用户名
     * @param series 存储用户和用例类型的 map，其中包含用例数据的整型数组
     * @param totalDay 总天数，用来作为存储用例数的数组长度
     * @param dateIndex 指定日期的索引，将用例数存到指定的日期
     * @param count 用例数量
     */
    private void doubleData(String type, String designer, Map<String, Map<String, Object>> series, int totalDay, int dateIndex, double count) {
        Map<String, Object> map = series.get(designer + type); // 从 series 获取数据（例如 key=张三_新增接口用例）
        if (map == null) { // 若果为空，创建一个新对象放入 map
            map = new HashMap<>();
            map.put("name", designer + type);
            map.put("stack", designer);
            double[] data = new double[totalDay]; // 用于存储对应日期中的数值
            data[dateIndex] = count; // 按照索引值将 data 插入数组
            map.put("data", data);
            series.put(designer + type, map);
        } else { // 不为空，插入对应日期的数值
            double[] data = (double[]) map.get("data");
            data[dateIndex] = count;
        }
    }

    @Override
    public Map<String, Object> getTotalCount(Long projectId) {
        int testSuiteCount = testSuiteDao.testSuiteCount(projectId); // 得到套件总数
        int funTestCaseCount = testCaseDao.testCaseCount(CatalogType.TEST_CASE, projectId); // 得到功能用例总数
        int apiTestCaseCount = testCaseDao.testCaseCount(CatalogType.INTERFACE_TEST_CASE, projectId); // 得到接口用例总数
        int apiCount = apiDao.apiCount(projectId); // 得到接口总数

        Map<String, Object> totalCountMap = new HashMap<>();
        totalCountMap.put("test_suite_count", testSuiteCount);
        totalCountMap.put("api_test_cases_count", apiTestCaseCount);
        totalCountMap.put("fun_test_cases_count", funTestCaseCount);
        totalCountMap.put("fun_api_count", apiCount);

        return totalCountMap;
    }
}
